#include "ModbusDlg.h"
#include <QComboBox>
#include <QGroupBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QRadioButton>
#include <QPushButton>
#include <QListWidget>
#include <QPlainTextEdit>
#include <qtablewidget>

#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QFile>

ModbusDlg::ModbusDlg(QWidget* parent)
{
	//mbPoll_ = new ModbusPoll();

	//initUI();
	setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint/* | Qt::WindowStaysOnTopHint*/);
	setWindowTitle("Modbus Settings");
}

void ModbusDlg::initUI(ModbusPoll * mb)
{
	// 如果
	if (mb == nullptr)
	{
		mb = new ModbusPoll();
	}
	mbPoll_ = mb;

	// 1.左侧modbus选择及参数设置
	// modbus连接类型选择
	QGroupBox* connectionBox = new QGroupBox("Connection", this);
	QComboBox* modbusConnType = new QComboBox(connectionBox);
	QStringList listModbus;
	listModbus << "Serial Master" << "Serial Slave" << "Tcp/Ip server" << "Tcp/Ip client";
	modbusConnType->addItems(listModbus);
	QPushButton* connectBtn = new QPushButton("connect", this);
	connectBtn->setFixedWidth(100);
	QHBoxLayout* connLayout = new QHBoxLayout();
	connLayout->addWidget(modbusConnType);
	connLayout->addWidget(connectBtn);
	connectionBox->setLayout(connLayout);
	
	// 串口设置
	QGroupBox* serialBox = (QGroupBox*)initSerialSettings();

	// tcp/ip设置
	// tcp/ip Server
	QGroupBox* tcpSettingsBox = (QGroupBox*)initTcpSettings();
	tcpSettingsBox->setEnabled(false);

	std::function<void(int)> funcConnType = [=](int index)
		{
			if (index == 0)
			{
				//qInfo() << "select modbus rtu serial";
				tcpSettingsBox->setEnabled(false);
				serialBox->setEnabled(true);
			}
			else
			{
				//qInfo() << "select modbus tcp/ip";
				tcpSettingsBox->setEnabled(true);
				serialBox->setEnabled(false);
			}
		};
	std::function<void()> funcConnect = [=]() {
		updateSettings();
		bool suc = mbPoll_->connect();
		if (suc)
			qInfo() << "modbus connected success.";
		else
			qInfo() << "modbus connected failed.";
		};
	connect(modbusConnType, &QComboBox::currentIndexChanged, funcConnType);
	connect(connectBtn, &QPushButton::clicked, funcConnect);
	

	QVBoxLayout* mainLayout = new QVBoxLayout();
	mainLayout->addWidget(connectionBox);
	mainLayout->addWidget(serialBox);
	mainLayout->addWidget(tcpSettingsBox);

	// 2.右侧widget初始化：关于modbus slave_id,data_address,data_num
	QWidget* rightWidget = initRegisterDataCtrl();

	// 3.左右布局加入到整个中心布局中
	QHBoxLayout* centerLayout = new QHBoxLayout();
	centerLayout->addLayout(mainLayout);
	centerLayout->addWidget(rightWidget);

	// 选择tcp/ip server
	switch (mbPoll_->getConnectType())
	{
	case tcp:
		modbusConnType->setCurrentIndex(2);
		break;
	case serial:
		modbusConnType->setCurrentIndex(0);
		break;
	default:
		qInfo() << "未知类型通讯协议.";
		break;
	}	

	setLayout(centerLayout);
}

void ModbusDlg::updateSettings()
{
	updateSerialSettigns_();
	updateTcpSettigns_();

	auto slaveId = findChild<QLineEdit*>("slave_id")->text().toInt();
	mbPoll_->setSlave(slaveId);
	mbPoll_->setSettings(&settings_);
}

QWidget* ModbusDlg::initSerialSettings()
{
	// serialport设置
	QGroupBox* serialBox = new QGroupBox("Serial Settings", this);
	// 1)串口名称
	QComboBox* commName = new QComboBox();
	QStringList listCommName;
	listCommName << "COM1" << "COM2" << "COM3" << "COM4" << "COM5" << "COM6" << "COM7" << "COM8";
	commName->addItems(listCommName);
	// 2)波特率
	QComboBox* baud = new QComboBox();
	QStringList listBaud;
	listBaud << "9600" << "115200";
	baud->addItems(listBaud);
	// 3）数据位
	QComboBox* dataBit = new QComboBox();
	QStringList listDataBit;
	listDataBit << "7" << "8";
	dataBit->addItems(listDataBit);
	// 4）校验位
	QComboBox* checkBit = new QComboBox();
	QStringList listCheckBit;
	listCheckBit << "None" << "Odd" << "Even";
	checkBit->addItems(listCheckBit);
	// 5）停止位
	QComboBox* stopBit = new QComboBox();
	QStringList listStopBit;
	listStopBit << "1" << "2";
	stopBit->addItems(listStopBit);
	//serialBox->setEnabled(false);

	QVBoxLayout* serialLyt = new QVBoxLayout();
	serialLyt->addWidget(commName);
	serialLyt->addWidget(baud);
	serialLyt->addWidget(dataBit);
	serialLyt->addWidget(checkBit);
	serialLyt->addWidget(stopBit);
	serialBox->setLayout(serialLyt);

	// 定义lamda函数：用于更新所有Serial设置
	updateSerialSettigns_ = std::bind([=]() {
		if (serialBox->isEnabled())
		{
			mbPoll_->setConnectType(serial);

			settings_.serialName = commName->currentText();
			settings_.baud = baud->currentText().toInt();
			settings_.dataBit = dataBit->currentText().toInt();
			auto f = [=] { int i = checkBit->currentIndex(); if (i == 0) { return 'N'; } else if (i == 1) { return 'O'; } else { return 'E'; } };
			settings_.parity = f();
			settings_.stopBit = stopBit->currentText().toInt();
		}		
		});

	if (mbPoll_->getConnectType() == serial)
	{

	}
	

	return serialBox;
}

QWidget* ModbusDlg::initTcpSettings()
{
	QGroupBox* tcpSettingsBox = new QGroupBox("tcp/ip Settings", this);
	QLabel* ipLabel = new QLabel("Remote TCP/IP:");
	QLineEdit* ipEdit = new QLineEdit("127.0.0.1", this);
	QLabel* portLabel = new QLabel("Port:");
	QLineEdit* portEdit = new QLineEdit("502", this);
	portEdit->setFixedWidth(50);
	QHBoxLayout* tcpBoxLyt = new QHBoxLayout();
	//tcpBoxLyt->addWidget(serverLabel);
	tcpBoxLyt->addWidget(ipLabel);
	tcpBoxLyt->addWidget(ipEdit);
	tcpBoxLyt->addWidget(portLabel);
	tcpBoxLyt->addWidget(portEdit);
	QRadioButton* tcpServer = new QRadioButton();
	QRadioButton* tcpClient = new QRadioButton();
	tcpSettingsBox->setLayout(tcpBoxLyt);
	tcpSettingsBox->setEnabled(false);

	// 定义lamda函数：用于更新所有Serial设置
	updateTcpSettigns_ = std::bind([=]() {
		if (tcpSettingsBox->isEnabled())
		{
			mbPoll_->setConnectType(tcp);
			settings_.addressIP = ipEdit->text();
			settings_.tcpPort = portEdit->text().toInt();
		}			
		});
	if (mbPoll_->getConnectType() == tcp)
	{
		auto settings = mbPoll_->getSettings();
		ipEdit->setText(settings->addressIP);
		portEdit->setText(QString::number(settings->tcpPort));
	}

	return tcpSettingsBox;
}

QWidget* ModbusDlg::initRegisterDataCtrl()
{
	// 返回组件
	QWidget* rightWidget = new QWidget();

	// 1.slaveid，startAddress，dataNums设置
	QLabel* slave = new QLabel("Slave Id:");
	slave->setFixedWidth(70);
	QLineEdit* slaveEdit = new QLineEdit("1");
	slaveEdit->setFixedWidth(50);
	slaveEdit->setObjectName("slave_id");
	QLabel* address = new QLabel("Address:");
	//address->setFixedWidth(50);
	QLineEdit* startAddr = new QLineEdit("0");
	startAddr->setFixedWidth(90);
	QLabel* num = new QLabel("Num:");
	//num->setFixedWidth(30);
	QLineEdit* dataNum = new QLineEdit("10");
	dataNum->setFixedWidth(50);
	int slaveId = mbPoll_->getSlaveId();
	slaveEdit->setText(QString::number(slaveId));

	// 布局
	QHBoxLayout* editLyt = new QHBoxLayout();
	editLyt->addWidget(slave);
	editLyt->addWidget(slaveEdit);
	editLyt->addWidget(address);
	editLyt->addWidget(startAddr);
	editLyt->addWidget(num);
	editLyt->addWidget(dataNum);

	// 2.功能码设置
	QComboBox* functionCode = new QComboBox();
	QStringList functionList;
	functionList << "01 Read Coils" << "02 Read Discret Inputs" << "03 Read Holding Registers" << "04 Read Input Registers" <<
		"05 Write Single Coils" << "06 Write Single Register" << "15 Write Multiple Coils" << "16 Write Multiple Registers";
	functionCode->addItems(functionList);

	// 3.数据表格创建及设置
	QTableWidget* dataTable = new QTableWidget(10,3);
	QStringList headerList = { "0","10","20" };
	dataTable->setHorizontalHeaderLabels(headerList);
	for (int r = 0; r < 10; r++)
	{
		for (int c = 0; c < headerList.size(); c++)
		{
			auto* item = new QTableWidgetItem("0");
			item->setTextAlignment(Qt::AlignRight);
			dataTable->setItem(r, c, item);
		}
	}
	dataTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	connect(startAddr, &QLineEdit::editingFinished, this, [=] {
		int a = startAddr->text().toInt() / 10 * 10;
		QStringList newHeaderList;
		newHeaderList << QString::number(a) << QString::number(a + 10) << QString::number(a + 20);
		dataTable->setHorizontalHeaderLabels(newHeaderList);
		});

	// 4.读写保存按钮
	QPushButton* rwBtn = new QPushButton("Read/Write");
	QPushButton* saveBtn = new QPushButton("Save");
	QHBoxLayout* rwBtnLyt = new QHBoxLayout();
	rwBtnLyt->addStretch();
	rwBtnLyt->addWidget(rwBtn);
	rwBtnLyt->addWidget(saveBtn);
	connect(saveBtn, &QPushButton::clicked, this, &ModbusDlg::on_saveModbusSet);

	// 5.布局整体组件
	QVBoxLayout* rightLyt = new QVBoxLayout();
	rightLyt->addLayout(editLyt);
	rightLyt->addWidget(functionCode);
	rightLyt->addWidget(dataTable);
	rightLyt->addLayout(rwBtnLyt);

	rightWidget->setLayout(rightLyt);

	connect(rwBtn, &QPushButton::clicked, this, [=] {
		int slaveId = slaveEdit->text().toInt();
		mbPoll_->setSlave(slaveId);
		int addr = startAddr->text().toInt();
		int n = dataNum->text().toInt();
		int funCode = functionCode->currentIndex();
		getTableData(funCode, dataTable);
		readAndWrite(funCode, addr, n, dataTable);
		});

	return rightWidget;
}

// 从表格获取需要发送的数据（写modbus）
void ModbusDlg::getTableData(int funCode, QTableWidget* table)
{
	if (funCode < write_single_coil)
	{
		memset(regDatas, 0, sizeof(uint16_t) * 30);
		memset(dataBits, 0, sizeof(uint8_t) * 30);
	}
	else
	{
		bool coilsData = false;
		if (funCode == write_single_coil ||
			funCode == write_multi_coils)
		{
			coilsData = true;
		}


		for (int c = 0; c < 3; c++)
		{
			for (int r = 0; r < 10; r++)
			{
				if (coilsData)
				{
					uint8_t d = static_cast<uint8_t>(table->item(r, c)->text().toInt());
					dataBits[r + c * 10] = d;
				}
				else
				{
					uint16_t d = static_cast<uint16_t>(table->item(r, c)->text().toInt());
					regDatas[r + c * 10] = d;
				}
			}
		}
	}
	return;
}

// 将读取的数据读取到表格
void ModbusDlg::fillData(int funCode, QTableWidget* table)
{
	for (int c = 0; c < 3; c++)
	{
		for (int r = 0; r < 10; r++)
		{
			if (funCode == read_coils)
			{
				table->item(r, c)->setData(Qt::DisplayRole, dataBits[r + c * 10]);
			}
			else
			{
				table->item(r, c)->setData(Qt::DisplayRole, regDatas[r + c * 10]);
			}			
		}
	}
}

void ModbusDlg::readAndWrite(int funCode, int addr, int n, QTableWidget* table)
{
	int r = 0;
	int a = addr % 10;
	switch (funCode)
	{
	case read_coils:
		r = mbPoll_->readCoils(dataBits + a, addr, n);
		fillData(read_coils, table);
		break;
	case read_discrete_inputs:
		qInfo() << "目前不支持";
		break;
	case read_holding_registers:
		r = mbPoll_->readRegisters(regDatas + a, addr, n);
		fillData(read_holding_registers, table);
		break;
	case read_input_registers:
		qInfo() << "目前不支持";
		break;
	case write_single_coil:
		r = mbPoll_->writeCoil(dataBits[a], addr);
		break;
	case write_single_register:
		r = mbPoll_->writeRegister(regDatas[a], addr);
		break;
	case write_multi_coils:
		r = mbPoll_->writeCoils(dataBits + a, addr, n);
		break;
	case write_multi_registers:
		r = mbPoll_->writeRegisters(regDatas + a, addr, n);
		break;
	default:
		qInfo() << "不支持该功能";
		break;
	}
}

void ModbusDlg::on_saveModbusSet()
{
	auto modbusFile = QApplication::applicationDirPath() + "//Config//modbus.json";
	updateSettings();
	mbPoll_->write(modbusFile.toStdString().data());
}
